<?php

namespace Swof\Routing;

use Swof\Http\Request;

class RouteCollection
{
    /**
     * An array of the routes keyed by method.
     *
     * @var array
     */
    protected $routes = [];

    /**
     * A flattened array of all of the routes.
     *
     * @var \Swof\Routing\Route[]
     */
    protected $allRoutes = [];

    /**
     * Note: 查找路由
     *
     * @param Request $request
     * @return Route|null
     * @author: Zhao Zhiqiang
     * @since: 2021/5/25 16:31
     */
    public function match(Request $request): ?Route
    {
        $routes = $this->get($request->getMethod());

        // First, we will see if we can find a matching route for this current request
        // method. If we can, great, we can just return it so that it can be called
        // by the consumer. Otherwise we will check for routes with another verb.
        return $this->matchAgainstRoutes($routes, $request);
    }

    /**
     * Note: 匹配路由
     *
     * @param array $routes
     * @param Request $request
     * @return Route|null
     * @author: Zhao Zhiqiang
     * @since: 2021/5/25 16:29
     */
    protected function matchAgainstRoutes(array $routes, Request $request): ?Route
    {
        $path = ltrim($request->getRequestUri(), '/') ?: '/';

        foreach ($routes as $route) {
            if (in_array($request->getMethod(), $route->methods())
                && $route->uri == $path) {
                return $route;
            }
        }

        return null;
    }

    /**
     * Get routes from the collection by method.
     *
     * @param string|null $method
     * @return \Swof\Routing\Route[]
     */
    public function get(string $method = null): array
    {
        return is_null($method)
            ? $this->getRoutes()
            : $this->routes[$method];
    }

    /**
     * Get all of the routes in the collection.
     *
     * @return \Swof\Routing\Route[]
     */
    public function getRoutes(): array
    {
        return array_values($this->allRoutes);
    }

    /**
     * Add a Route instance to the collection.
     *
     * @param  \Swof\Routing\Route  $route
     * @return \Swof\Routing\Route
     */
    public function add(Route $route): Route
    {
        $this->addToCollections($route);

        return $route;
    }

    /**
     * Add the given route to the arrays of routes.
     *
     * @param  \Swof\Routing\Route  $route
     * @return void
     */
    protected function addToCollections(Route $route)
    {
        $domainAndUri = $route->getDomain().$route->uri();

        foreach ($route->methods() as $method) {
            $this->routes[$method][$domainAndUri] = $route;
        }

        $this->allRoutes[$method.$domainAndUri] = $route;
    }
}